package com.wingtech.sheyifan.device;

import com.wingtech.sheyifan.shared.Logger;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;

public class LogOptions {
    boolean ifconfig = true;
    boolean iptable = true;
    boolean iproute = true;
    boolean dns = true;
    boolean sim = true;
    boolean qcmap = true;
    boolean ping = true;
    boolean systemLog = true;
    boolean boot = true;
    boolean version = true;
    boolean adbLog = true;
    boolean tcpdump = true;
    boolean tmp = true;

    Path des;

    public LogOptions(boolean ifconfig,
                      boolean iptable,
                      boolean iproute,
                      boolean dns,
                      boolean sim,
                      boolean qcmap,
                      boolean ping,
                      boolean systemLog,
                      boolean boot,
                      boolean version,
                      boolean adbLog,
                      boolean tcpdump,
                      boolean tmp,
                      Path des) {
        this.ifconfig = ifconfig;
        this.iptable = iptable;
        this.iproute = iproute;
        this.dns = dns;
        this.sim = sim;
        this.qcmap = qcmap;
        this.ping = ping;
        this.systemLog = systemLog;
        this.boot = boot;
        this.version = version;
        this.adbLog = adbLog;
        this.tcpdump = tcpdump;
        this.tmp = tmp;

        this.des = des;
    }

    public void exportIpConfig() {
        var ifconfigDes = des.resolve("logs").resolve("network").resolve("ifconfig.txt");

        try {
            redirectCLIToFileSystem("adb shell ifconfig", ifconfigDes, false);
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void exportIptable() {
        var iptableDes = des.resolve("logs").resolve("network").resolve("iptable.txt");

        try {
            redirectCLIToFileSystem("adb shell iptables -nvL", iptableDes, false);
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }

        exportIptable1();
    }

    private void exportIptable1() {
        var iptableDes1 = des.resolve("logs").resolve("network").resolve("iptable.txt");

        try {
            redirectCLIToFileSystem("adb shell iptables -t nat -nvL", iptableDes1, true);
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void exportIproute() {
        var iprouteDes = des.resolve("logs").resolve("network").resolve("iproute.txt");

        try {
            redirectCLIToFileSystem("adb shell iproute", iprouteDes, false);
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void exportDNSInfo() {
        var dnsDes = des.resolve("logs").resolve("network").resolve("DNS.txt");

        try {
            redirectCLIToFileSystem("adb shell cat /etc/resolv.conf", dnsDes, false);
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void exportSIMStatus() {
        var simDes = des.resolve("logs").resolve("network").resolve("SIM.txt");

        try {
            redirectCLIToFileSystem("adb shell cat /tmp/sim_status", simDes, false);
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void exportQCMAPPID() {
        var qcmapWebClientIdDes = des.resolve("logs").resolve("network").resolve("QCMAP_Web_CLIENT.txt");

        try {
            redirectCLIToFileSystem("adb shell pidof QCMAP_Web_CLIENT", qcmapWebClientIdDes, false);
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void exportPingLog() {
        var pingDes = des.resolve("logs").resolve("network").resolve("ping.txt");

        try {
            redirectCLIToFileSystem("adb shell ping -n 1 127.0.0.1", pingDes, false);
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void exportSystemLog() {
        var systemLogDes = des.resolve("logs").resolve("system").resolve("system_log.txt");

        try {
            redirectCLIToFileSystem("adb shell cat /var/log/messages", systemLogDes, false);
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void exportBootLog() {
        var bootLogDes = des.resolve("logs").resolve("system").resolve("boot.txt");

        try {
            redirectCLIToFileSystem("adb shell dmesg", bootLogDes, false);
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void exportVersionLog() {
        var versionLogDes = des.resolve("logs").resolve("system").resolve("version.txt");

        try {
            redirectCLIToFileSystem("adb shell cat /etc/wt_version", versionLogDes, false);
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void exportAdbLog() {
        var adbLogDir = des.resolve("logs").resolve("adb");
        var adbPullLog = des.resolve("logs").resolve("adb").resolve("adbpull.txt");
        String command = "adb pull /data/logs/ \"" + adbLogDir.toString() + "\"";

        try {
            redirectCLIToFileSystem(command, adbPullLog, false);
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void exportTcpDump() {
        var tcpDumpLogDes = des.resolve("logs").resolve("network").resolve("tcpdump").resolve("tcpdump.txt");

        try {
            redirectCLIToFileSystem("adb shell tcpdump -i any -w /data/p.pcap -c 1", tcpDumpLogDes, false);

            var proc = Runtime.getRuntime().exec("adb pull /data/p.pcap " + des.resolve("logs").resolve("network").resolve("tcpdump"));
            proc.waitFor();
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void exportTmpLog() {
        var tmpPullLog = des.resolve("logs").resolve("others").resolve("tmppull.txt");
        var tmpPullDir = des.resolve("logs").resolve("others");

        String command = "adb pull /tmp \"" + tmpPullDir.toString() + "\"";

        try {
            redirectCLIToFileSystem(command, tmpPullLog, false);
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void redirectCLIToFileSystem(String command, Path des, boolean append) throws IOException, InterruptedException {
        Date now = new Date();
        SimpleDateFormat dateFormater = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        if (Files.notExists(des.getParent())) {
            Files.createDirectories(des.getParent());
        }

        if (Files.notExists(des)) {
            Files.createFile(des);
        }
        else {
            Logger.printToolLog("Warning: will override " + des);
        }

        final boolean[] reading = new boolean[1];
        reading[0] = true;
        final BufferedWriter[] writer = new BufferedWriter[1];
        var proc = Runtime.getRuntime().exec(command);

        var consoleListener = new Thread(() -> {
            int i = 0;
            var reader = new BufferedReader(new InputStreamReader(proc.getInputStream()));
            try {
                writer[0] = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(des.toFile(), append)));
                writer[0].write("=====================================================================");
                writer[0].newLine();
                writer[0].write("COMMAND: " + command);
                writer[0].newLine();
                writer[0].write(dateFormater.format(now));
                writer[0].newLine();
                writer[0].write("=====================================================================");
                writer[0].newLine();
            } catch (IOException e) {
                e.printStackTrace();
            }
            String buf;

            while (true) {
                try {
                    if ((buf = reader.readLine()) != null && writer[0] != null) {
                        writer[0].write(buf);
                        writer[0].newLine();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }

                if(!reading[0]) {
                    if (writer[0] != null) {
                        try {
                            writer[0].close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }

                    break;
                }
            }
        });
        consoleListener.start();

        var consoleErrListener = new Thread(() -> {
            var errReader = new BufferedReader(new InputStreamReader(proc.getErrorStream()));
            String buf;

            while (true) {
                try {
                    if ((buf = errReader.readLine()) != null && writer[0] != null) {
                        Logger.printToolError(buf);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }

                if(!reading[0]) {
                    break;
                }
            }
        });
        consoleErrListener.start();

        proc.waitFor();
        reading[0] = false;
    }
}
